package com.sudo.boot.service.sys.impl;

import cn.dev33.satoken.session.SaSession;
import cn.dev33.satoken.stp.SaLoginModel;
import cn.dev33.satoken.stp.StpUtil;
import com.boot.starter.redis.component.RedisKeys;
import com.mybatisflex.core.paginate.Page;
import com.mybatisflex.core.query.If;
import com.mybatisflex.core.query.QueryWrapper;
import com.mybatisflex.spring.service.impl.ServiceImpl;
import com.sudo.boot.base.constant.SsoConstant;
import com.sudo.boot.base.constant.SymbolConstant;
import com.sudo.boot.base.constant.SysConstant;
import com.sudo.boot.base.constant.enums.EnumStatus;
import com.sudo.boot.base.constant.enums.EnumYesNo;
import com.sudo.boot.base.exception.BizException;
import com.sudo.boot.base.util.IfFail;
import com.sudo.boot.base.util.UuidUtil;
import com.sudo.boot.common.constant.sysconfig.RoleConfig;
import com.sudo.boot.mapper.sys.entity.SysRole;
import com.sudo.boot.mapper.sys.entity.SysUser;
import com.sudo.boot.mapper.sys.entity.SysUserRole;
import com.sudo.boot.mapper.sys.mapper.SysRoleMapper;
import com.sudo.boot.mapper.sys.mapper.SysUserMapper;
import com.sudo.boot.mapper.sys.mapper.SysUserRoleMapper;
import com.sudo.boot.mapper.sys.model.bo.*;
import com.sudo.boot.mapper.sys.model.vo.*;
import com.sudo.boot.mapper.wx.mapper.WxUserMapper;
import com.sudo.boot.mybatisflex.annotation.IdToName;
import com.sudo.boot.satokensso.util.BootStpUtil;
import com.sudo.boot.satokensso.util.SaRedisUtil;
import com.sudo.boot.service.sys.SysConfigService;
import com.sudo.boot.service.sys.SysUserService;
import com.sudo.boot.service.sys.converter.SysUserConverter;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import static com.sudo.boot.mapper.sys.entity.table.SysRoleTableDef.SYS_ROLE;
import static com.sudo.boot.mapper.sys.entity.table.SysUserRoleTableDef.SYS_USER_ROLE;
import static com.sudo.boot.mapper.sys.entity.table.SysUserTableDef.SYS_USER;

/**
 * 系统用户  服务层实现。
 *
 * @author admins
 * @since 2023-10-13
 */
@Slf4j
@Service
public class SysUserServiceImpl extends ServiceImpl<SysUserMapper, SysUser> implements SysUserService {

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;
    @Resource
    private SysRoleMapper sysRoleMapper;
    @Resource
    private SysUserConverter sysUserConverter;
    @Resource
    private SysRedisService sysRedisService;
    @Resource
    private SaRedisUtil saRedisUtil;
    @Resource
    private SysConfigService sysConfigService;
    @Resource
    private WxUserMapper wxUserMapper;

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void add(SysUserBoAdd bo) {
        // 角色数量校验
        Map<String,Object> configMap = sysConfigService.getSysConfigMapByKey(RoleConfig.class);
        int roleCount = Integer.parseInt(configMap.get(RoleConfig.roleCount).toString());
        if (bo.getRoleCodeList().size() > roleCount) {
            throw new BizException("用户最多可设置" + roleCount + "个角色");
        }
        // 判断用户名是否被占用
        long userCount = mapper.selectCountByCondition(SYS_USER.USERNAME.eq(bo.getUsername()));
        Assert.isTrue(userCount <= 0, "该用户名已被占用");

        // 不能设为超级角色
        if (bo.getRoleCodeList().contains(SysConstant.super_role)) {
            throw new BizException("不能设为超级角色");
        }

        // 判断角色是否存在
        judgeRoleExist(bo.getRoleCodeList());

        // 新增用户
        SysUser sysUser = new SysUser();
        sysUser.setUsername(bo.getUsername());
        sysUser.setNickName(bo.getNickName());
        sysUser.setNote(bo.getNote());
        sysUser.setStatus(bo.getStatus());
        sysUser.setPassword(BootStpUtil.encryptPwd(bo.getPassword()));
        sysUser.setPassword(bo.getPassword());

        mapper.insert(sysUser);

        // 新增用户、角色关联关系
        List<SysUserRole> userRoleList = new ArrayList<>();
        for (String roleCode : bo.getRoleCodeList()) {
            SysUserRole userRole = new SysUserRole();
            userRole.setUsername(bo.getUsername());
            userRole.setRoleCode(roleCode);
            userRole.setDefaultRole(EnumYesNo.NO.getCode());
            userRoleList.add(userRole);
        }
        // 设置默认默认角色
        userRoleList.get(0).setDefaultRole(EnumYesNo.YES.getCode());
        IfFail.dealInt(() -> sysUserRoleMapper.insertBatch(userRoleList));
    }

    private void judgeRoleExist(List<String> roleCodeBoList) {
        List<SysRole> sysRoleList = sysRoleMapper.selectListByCondition(SYS_ROLE.ROLE_CODE.in(roleCodeBoList));
        if (CollectionUtils.isEmpty(sysRoleList)) {
            throw new BizException("角色不存在");
        }
        List<String> roleCodeList = sysRoleList.stream().map(SysRole::getRoleCode).toList();
        for (String webRoleCode : roleCodeBoList) {
            if (!roleCodeList.contains(webRoleCode)) {
                throw new BizException("角色：" + webRoleCode + " 不存在");
            }
        }
    }

    @Override
    public Page<SysUserPageVo> page(SysUserBoPageQuery bo) {
        Page<SysUserPageVo> page = Page.of(bo.getPageNumber(), bo.getPageSize());
        QueryWrapper queryWrapper = QueryWrapper.create()
                .where(SYS_USER.USERNAME.like(bo.getUsername(), If::hasText))
                .and(SYS_USER.NICK_NAME.like(bo.getNickName(),If::hasText))
                .and(SYS_USER.STATUS.eq(bo.getStatus(),If::hasText));
        Page<SysUserPageVo> userPage = mapper.paginateAs(page, queryWrapper, SysUserPageVo.class);
        if (CollectionUtils.isEmpty(userPage.getRecords())) {
            return page;
        }

        // 根据用户名查询角色
        List<String> usernameList = userPage.getRecords().stream().map(SysUserPageVo::getUsername).toList();
        QueryWrapper userRoleQuery = QueryWrapper.create()
                .select(SYS_ROLE.ROLE_CODE.as(SYS_ROLE.ROLE_CODE.getName()),
                        SYS_ROLE.ROLE_NAME,
                        SYS_ROLE.STATUS,
                        SYS_USER_ROLE.USERNAME,
                        SYS_USER_ROLE.DEFAULT_ROLE)
                .from(SYS_USER_ROLE)
                .leftJoin(SYS_ROLE).on(SYS_USER_ROLE.ROLE_CODE.eq(SYS_ROLE.ROLE_CODE))
                .where(SYS_USER_ROLE.USERNAME.in(usernameList));
        List<SysUserRoleVo> userRoleVoList = mapper.selectListByQueryAs(userRoleQuery, SysUserRoleVo.class);
        Map<String, List<SysUserRoleVo>> userRoleMap = userRoleVoList.stream()
                .collect(Collectors.groupingBy(SysUserRoleVo::getUsername));

        // 数据拼装
        for (SysUserPageVo sysUser : userPage.getRecords()) {
            sysUser.setUserRoleVOList(userRoleMap.get(sysUser.getUsername()));
        }

        return userPage;
    }

    @IdToName
    @Override
    public SysUserDetailVo detailById(Serializable id) {
        SysUserDetailVo detailVo = mapper.selectOneWithRelationsByIdAs(id, SysUserDetailVo.class);
        Assert.notNull(detailVo, "用户不存在");

        // 根据用户名查询角色
        QueryWrapper userRoleQuery = QueryWrapper.create()
                .select(SYS_ROLE.ROLE_CODE.as(SYS_ROLE.ROLE_CODE.getName()),
                        SYS_ROLE.ROLE_NAME,
                        SYS_ROLE.STATUS,
                        SYS_USER_ROLE.USERNAME,
                        SYS_USER_ROLE.DEFAULT_ROLE)
                .from(SYS_USER_ROLE)
                .leftJoin(SYS_ROLE).on(SYS_USER_ROLE.ROLE_CODE.eq(SYS_ROLE.ROLE_CODE))
                .where(SYS_USER_ROLE.USERNAME.in(detailVo.getUsername()));
        List<SysUserRoleVo> userRoleVoList = mapper.selectListByQueryAs(userRoleQuery, SysUserRoleVo.class);
        // 默认角色
        for (SysUserRoleVo userRoleVo : userRoleVoList) {
            if (EnumYesNo.YES.getCode().equals(userRoleVo.getDefaultRole())) {
                detailVo.setDefaultRoleCode(userRoleVo.getRoleCode());
                break;
            }
        }

        // 数据拼装
        detailVo.setUserRoleVOList(userRoleVoList);
        return detailVo;
    }

    @Override
    public SysUserSimpleInfoVo simpleInfoById(Serializable id) {
        SysUser sysUser = mapper.selectOneById(id);
        if (sysUser == null) {
            throw new BizException("用户不存在");
        }

        QueryWrapper queryWrapper = QueryWrapper.create()
                .from(SYS_USER_ROLE)
                .where(SYS_USER_ROLE.USERNAME.eq(sysUser.getUsername()));
        List<SysUserRole> sysUserRoleList = mapper.selectListByQueryAs(queryWrapper,SysUserRole.class);

        if (CollectionUtils.isEmpty(sysUserRoleList)) {
            throw new BizException("该用户没有角色，请联系管理员");
        }
        SysUserSimpleInfoVo detailVo = sysUserConverter.toSysUserDetailVo(sysUser);
        List<String> roleCodeList = sysUserRoleList.stream().map(SysUserRole::getRoleCode).toList();
        detailVo.setRoleCodeList(roleCodeList);
        return detailVo;
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void edit(SysUserBoEdit bo) {

        // 不能修改超级用户
        SysUser sysUser = mapper.selectOneById(bo.getId());
        if (SysConstant.super_user.contains(sysUser.getUsername())) {
            throw new BizException("不能修改超级用户");
        }
        // 角色数量校验
        Map<String,Object> configMap = sysConfigService.getSysConfigMapByKey(RoleConfig.class);
        int roleCount = Integer.parseInt(configMap.get(RoleConfig.roleCount).toString());
        if (bo.getRoleCodeList().size() > roleCount) {
            throw new BizException("用户最多可设置" + roleCount + "个角色");
        }

        // 判断角色是否存在
        judgeRoleExist(bo.getRoleCodeList());

        // 不能设为超级角色
        if (bo.getRoleCodeList().contains(SysConstant.super_role)) {
            throw new BizException("不能设为超级角色");
        }

        // 1.编辑用户
        SysUser newUser = new SysUser();
        newUser.setId(bo.getId());
        newUser.setNickName(bo.getNickName());
        newUser.setNote(bo.getNote());
        IfFail.dealInt(() -> mapper.update(newUser));


        // 2.删除角色关联关系
        int userRoleCount = sysUserRoleMapper.deleteByCondition(SYS_USER_ROLE.USERNAME.eq(sysUser.getUsername()));
        IfFail.dealInt(() -> userRoleCount);

        // 3.新增角色关联关系
        List<SysUserRole> userRoleList = new ArrayList<>();
        for (String roleCode : bo.getRoleCodeList()) {
            SysUserRole userRole = new SysUserRole();
            userRole.setUsername(sysUser.getUsername());
            userRole.setRoleCode(roleCode);
            userRole.setDefaultRole(EnumYesNo.NO.getCode());
            userRoleList.add(userRole);
        }
        // 4.设置默认角色
        userRoleList.get(0).setDefaultRole(EnumYesNo.YES.getCode());

        // 5.插入角色关联关系
        IfFail.dealInt(() -> sysUserRoleMapper.insertBatch(userRoleList));
    }

    @Override
    public void status(SysUserBoStatus bo) {

        // 不能修改超级管理员
        SysUser sysUser = mapper.selectOneById(bo.getId());
        if (sysUser.getUsername().equals(SysConstant.super_user)) {
            throw new BizException("不能修改超级用户");
        }
        // 不能切换自己的状态
        String currUsername = BootStpUtil.getCurrUsername();
        Assert.isTrue(!currUsername.equals(sysUser.getUsername()), "不能切换自己的状态");

        SysUser statusUser = new SysUser();
        statusUser.setId(bo.getId());
        statusUser.setStatus(bo.getStatus());

        // 1.切换状态
        IfFail.dealInt(() -> mapper.update(statusUser));

        // 修改缓存
        sysRedisService.removeUserStatus(sysUser.getUsername());
    }

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = Exception.class)
    @Override
    public void deleteById(Serializable id) {
        SysUser sysUser = mapper.selectOneById(id);
        // 不能删除超级用户
        if (sysUser.getUsername().equals(SysConstant.super_user)) {
            throw new BizException("不能删除超级用户");
        }

        // 1.删除用户
        IfFail.dealInt(() -> mapper.deleteById(id));


        // 2.删除角色关联关系
        int deleteCount = sysUserRoleMapper.deleteByCondition(SYS_USER_ROLE.USERNAME.eq(sysUser.getUsername()));

        IfFail.dealInt(() -> deleteCount);

        // 删除用户缓存信息
        StpUtil.getSessionByLoginId(sysUser.getUsername()).clear();
    }

    @Override
    public Page<SysUserSimpleVo> pageByRoleId(SysUserBoPageQuery bo) {
        // 查询角色
        SysRole sysRole = sysRoleMapper.selectOneById(bo.getRoleId());
        Assert.notNull(sysRole, "角色不存在");

        Page<SysUserSimpleVo> page = Page.of(bo.getPageNumber(), bo.getPageSize());
        QueryWrapper userQuery = QueryWrapper.create()
                .select(SYS_USER.USERNAME.as(SYS_USER.USERNAME.getName()),
                        SYS_USER.NICK_NAME,
                        SYS_USER.STATUS,
                        SYS_USER.ID.as(SYS_USER.ID.getName()),
                        SYS_USER.CREATE_BY,
                        SYS_USER.CREATE_TIME)
                .from(SYS_USER_ROLE)
                .leftJoin(SYS_USER).on(SYS_USER.USERNAME.eq(SYS_USER_ROLE.USERNAME))
                .where(SYS_USER_ROLE.ROLE_CODE.eq(sysRole.getRoleCode()))
                .and(SYS_USER.USERNAME.like(bo.getUsername(),If::hasText))
                .and(SYS_USER.NICK_NAME.like(bo.getNickName(),If::hasText))
                .and(SYS_USER.STATUS.eq(bo.getStatus(),If::hasText));
        return mapper.paginateAs(page, userQuery, SysUserSimpleVo.class);
    }

    @Override
    public void resetPassword(SysUserBoPassword bo) {
        SysUser sysUser = mapper.selectOneById(bo.getId());
        // 不能重置超级用户
        if (sysUser.getUsername().equals(SysConstant.super_user)) {
            throw new BizException("不能重置超级用户");
        }

        // 判断用户新密码与确认密码是否一致
        Assert.isTrue(bo.getNewPassword().equals(bo.getConfirmPassword()),"新密码与确认密码不一致");

        SysUser passwordUser = new SysUser();
        passwordUser.setId(bo.getId());
        passwordUser.setPassword(BootStpUtil.encryptPwd(bo.getNewPassword()));
        IfFail.dealInt(() -> mapper.update(passwordUser));
    }

    @Override
    public String simulateLogin(SysUserBoSimulateLogin bo) {
        // 判断当前用户是否为模拟用户，如果是模拟用户则不能模拟其他用户登录
        String tokenValue = StpUtil.getTokenValue();
        if (tokenValue.contains(SymbolConstant.DOT)) {
            throw new BizException("当前用户为模拟用户，不能继续模拟");
        }

        // 判断是否已经在模拟登陆中，如果已经处于模拟登陆中则不能继续模拟
        String currUsername = String.valueOf(StpUtil.getLoginId());

        // 判断用户是否存在
        SysUser sysUser = mapper.selectOneById(bo.getId());
        Assert.notNull(sysUser, "该用户不存在");
        // 不能模拟超级用户
        if (sysUser.getUsername().equals(SysConstant.super_user)) {
            throw new BizException("不能模拟超级用户");
        }

        // 不能模拟自身用户
        if (currUsername.equals(sysUser.getUsername())) {
            throw new BizException("不能模拟自身登陆");
        }
        // 用户状态
        if (!EnumStatus.ON.getCode().equals(sysUser.getStatus())) {
            throw new BizException("该用户已锁定");
        }

        // 查询用户信息
        String roleCode = getCurrRoleByUsername(sysUser.getUsername());

        // 登陆，并设置模拟标签
        SaLoginModel saLoginModel = new SaLoginModel();
        saLoginModel.setToken(currUsername + SymbolConstant.DOT +UuidUtil.newUuid());
        saLoginModel.setTimeout(600);
        StpUtil.login(sysUser.getUsername(),saLoginModel);

        SaSession saSession = StpUtil.getSessionByLoginId(sysUser.getUsername());
        // 设置用户ID
        saSession.set(SsoConstant.currUserId, sysUser.getId());

        // 设置用户当前登录角色
        saSession.set(SsoConstant.currRoleCode, roleCode);

        return StpUtil.getTokenValueByLoginId(sysUser.getUsername());
    }



    private String getCurrRoleByUsername(String username) {

        // 查询用户角色关联关系
        List<SysUserRole> userRoleList = sysUserRoleMapper.selectListByCondition(SYS_USER_ROLE.USERNAME.eq(username));
        Assert.notEmpty(userRoleList, "暂无角色，请联系管理员");

        // 查询角色
        List<String> roleCodeList = userRoleList.stream().map(SysUserRole::getRoleCode).toList();
        QueryWrapper queryWrapper = QueryWrapper.create()
                .select(SYS_USER_ROLE.USERNAME,SYS_USER_ROLE.ROLE_CODE,SYS_USER_ROLE.DEFAULT_ROLE,SYS_ROLE.STATUS)
                .from(SYS_USER_ROLE)
                .leftJoin(SYS_ROLE).on(SYS_USER_ROLE.ROLE_CODE.eq(SYS_ROLE.ROLE_CODE))
                .where(SYS_ROLE.ROLE_CODE.in(roleCodeList));
        List<SysUserRoleVo> roleSimpleVoList = sysRoleMapper.selectListByQueryAs(queryWrapper, SysUserRoleVo.class);

        // 默认角色
        SysUserRoleVo defaultRole = null;
        List<SysUserRoleVo> otherRoleList = new ArrayList<>();
        for (SysUserRoleVo loginDto : roleSimpleVoList) {
            if (EnumYesNo.YES.getCode().equals(loginDto.getDefaultRole())) {
                defaultRole = loginDto;
            }else {
                otherRoleList.add(loginDto);
            }
        }
        Assert.notNull(defaultRole,"缺少默认角色，请联系管理员");


        // 如果默认角色没有禁用，则直接登录
        if (EnumStatus.ON.getCode().equals(defaultRole.getStatus())) {
            // 写入当前用户角色信息
            String roleStatus = RedisKeys.roleStatus(defaultRole.getRoleCode());
            if (Objects.isNull(saRedisUtil.get(roleStatus))) {
                saRedisUtil.set(roleStatus, EnumStatus.ON.getCode());
            }
            return defaultRole.getRoleCode();
        }

        // 如果默认角色已禁用，则找到切换角色登录
        // 判断是否存在其他角色
        Assert.notEmpty(otherRoleList,"该帐号唯一角色被禁用，禁止登录");

        // 找到未禁用的角色
        List<SysUserRoleVo> roleOnStatus = otherRoleList.stream()
                .filter(o -> EnumStatus.ON.getCode().equals(o.getStatus())).toList();
        Assert.notEmpty(roleOnStatus,"该帐号所有角色被禁用，禁止登录");

        // 随机获取一个角色登录
        String currRoleCode = roleOnStatus.get(0).getRoleCode();
        // 写入当前用户角色信息
        String roleStatus = RedisKeys.roleStatus(currRoleCode);
        // 角色状态
        if (Objects.isNull(saRedisUtil.get(roleStatus))) {
            saRedisUtil.set(roleStatus, EnumStatus.ON.getCode());
        }
        return currRoleCode;
    }

    @Override
    public String viewPassword(Integer id) {
        SysUser sysUser = mapper.selectOneById(id);
        Assert.notNull(sysUser, "用户不存在");

        // 不能模拟超级用户
        if (sysUser.getUsername().equals(SysConstant.super_user)) {
            throw new BizException("不能解密超级用户");
        }
        // 解密
        return BootStpUtil.decryptPwd(sysUser.getPassword());
    }

}
